﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace IcqSharp
{
    public class Packet
    {
        public MemoryStream Data;
        public Channel Channel;

        public void Init(Channel c)
        {
            Data = new MemoryStream();
            Channel = c;

            Data.Write((Byte)0x2A);       // FLAP marker
            Data.Write((Byte)c);          // Channel ID
            Data.Write((UInt16)0x0000);   // post-edit: LocalSequence
            Data.Write((UInt16)0x0000);   // post-edit: Length, w/0 header (6 Bytes)
        }
        public Packet(Channel c = Channel.Data)
        {
            Init(c);
        }

        public void WriteFNACHeader(UInt16 Family, UInt16 Subtype, UInt16 Flags, UInt32 Sequence, UInt16 Version)
        {
            WriteFNACHeader(Family, Subtype, (UInt16)(Flags | 0x8000), Sequence);
            Data.Write((UInt16)0x06);
            Data.WriteTLV(0x01, Version);
        }
        public void WriteFNACHeader(UInt16 Family, UInt16 Subtype, UInt16 Flags, UInt32 Sequence)
        {
            // this is necessary, if that bit is there we get disconnected
            var wSequence = (UInt16)Sequence & 0x7FFF;

            Data.Write((UInt16)Family);   // Family type
            Data.Write((UInt16)Subtype);  // Family subtype
            Data.Write((UInt16)Flags);    // SNAC flags
            Data.Write((UInt16)Sequence); // SNAC request id (sequence)
            Data.Write((UInt16)(Sequence >> 0x10)); // SNAC request id (command)
        }
        public void WriteFNACHeader(UInt16 Family, UInt16 Subtype)
        {
            WriteFNACHeader(Family, Subtype, 0, (UInt16)(Subtype << 0x10));
        }


        public void WriteUID(UInt32 uin, String uid)
        {
            var s = (uin != 0) ? uin.ToString() : uid;

            Data.Write((Byte)s.Length);
            Data.Write(s.GetAsciiBytes());
        }


        public void WriteMsgSendHeader(UInt32 Sequence, UInt32 ID1, UInt32 ID2, UInt32 UIN, String UID, UInt16 Format, UInt16 Len)
        {
            WriteFNACHeader((UInt16)Family.Message, (UInt16)MessageFamilySubtype.SrvSend, 
                0, Sequence | (UInt32)MessageFamilySubtype.SrvSend << 0x10);

            Data.WriteLE(ID1);      // Msg ID part 1
            Data.WriteLE(ID2);      // Msg ID part 2
            Data.Write(Format);     // Message channel
            WriteUID(UIN, UID);     // User ID
        }


        public void WriteServIcqExtensionHeader(UInt16 Len, UInt16 Type, UInt16 Seq, UInt16 Cmd = (UInt16)ExtensionsFamilySubtype.CliRequest)
        {
            WriteFNACHeader((UInt16)Family.Extensions, 
                (UInt16)ExtensionsFamilySubtype.CliRequest, 
                0, (UInt32)Seq | ((UInt32)Cmd<<0x10));

            Data.Write((UInt16)0x01);               // TLV type 1
            Data.Write((UInt16)(10 + Len));         // TLV len
            Data.WriteLE((UInt16)(8 + Len));        // Data chunk size (TLV.Length-2)
            Data.WriteLE((UInt32)Session.LocalUIN); // UIN 
            Data.WriteLE((UInt16)Type);             // Request type
            Data.Write(Seq);
        }


        public void packServIcqDirectoryHeader(UInt16 Len, UInt16 Type, UInt16 Command, UInt16 Seq, UInt16 SubCommand = (UInt16)ExtensionsFamilySubtype.CliRequest)
        {
            WriteServIcqExtensionHeader((UInt16)(Len + 0x1E), (UInt16)0x07D0, Seq, SubCommand);

            Data.WriteLE(Type);
            Data.WriteLE((UInt16)(Len + 0x1A));

            WriteFNACHeader(0x5B9, Command, 0, 0, 2);

            Data.Write((UInt16)0);
            Data.Write((UInt16)1251);
            Data.Write((UInt32)2);
        }


        public void WriteCookie(Byte[] cookie)
        {
            Data.Write((UInt32)0x00000001);
            Data.WriteTLV(0x06, cookie);

            // client identification details
            Data.WriteTLV(0x0003, ClientDescription.IdString.GetAsciiBytes());
            Data.WriteTLV(0x0017, ClientDescription.VersionMajor);
            Data.WriteTLV(0x0018, ClientDescription.VersionMinor);
            Data.WriteTLV(0x0019, ClientDescription.VersionLesser);
            Data.WriteTLV(0x001A, ClientDescription.VersionBuild);
            Data.WriteTLV(0x0016, ClientDescription.IdCode);
            Data.WriteTLV(0x0014, ClientDescription.Distribution);
            Data.WriteTLV(0x000F, ClientDescription.Language.GetAsciiBytes());
            Data.WriteTLV(0x000E, ClientDescription.Country.GetAsciiBytes());

            // flags
            Data.Write((UInt32)0x00940001); // reconnect flag
            Data.Write((Byte)0);
            Data.WriteTLV(0x8003, (UInt32)0x00100000); // Unknown
        }

        public void WriteShortCapability(UInt16 capability)
        { 
            // pack standard capability
            var Q1 = 0x09460000 | (UInt32)capability;

            Data.Write((UInt32)Q1);
            Data.Write((UInt32)0x4c7f11d1);
            Data.Write((UInt32)0x82224445);
            Data.Write((UInt32)0x53540000);
        }



        public void Send()
        {
            Session.SendPacket(this);
        }
    }
}
